﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace Sio.Mdm.Entities.Linq.Factory
{
    public static class FilterFactory
    {
        //malo telovadbe ker so naše entitete interfacei
        private static PropertyInfo FindMemberField<T>(string prop)
        {
            /*PropertyInfo[] pis = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
            Type[] tt = typeof(T).GetInterfaces();
            PropertyInfo[] pis2 = tt[0].GetProperties(BindingFlags.Public | BindingFlags.Instance);
            return pis2.Where(r => r.MemberType == System.Reflection.MemberTypes.Property).Where(r => r.Name.Equals(prop)).First();
            */
            var pis2 = Sio.Mdm.Client.Helper.FlattenProperties(typeof(T));
            return pis2.Where(r => r.MemberType == System.Reflection.MemberTypes.Property).Where(r => r.Name.Equals(prop)).First();
        }

        public static ISioFilter<T> AddOrderBy<T>(this ISioFilter<T> source, string prop)
        {
            ParameterExpression p = Expression.Parameter(typeof(T), "r");

            MemberExpression m = Expression.MakeMemberAccess(p, FindMemberField<T>(prop));

            LambdaExpression pred = Expression.Lambda(m, p );

            return source.CreateQuery<T>(
                 Expression.Call(typeof(FilterMethods), "OrderBy", new Type[] { typeof(T), ((PropertyInfo)m.Member).PropertyType }, source.Expression, pred)
                  );
        }

        public static ISioFilter<T> AddOrderByDescending<T>(this ISioFilter<T> source, string prop)
        {
            ParameterExpression p = Expression.Parameter(typeof(T), "r");

            MemberExpression m = Expression.MakeMemberAccess(p, FindMemberField<T>(prop));

            LambdaExpression pred = Expression.Lambda(m, p);

            return source.CreateQuery<T>(
                 Expression.Call(typeof(FilterMethods), "OrderByDescending", new Type[] { typeof(T), ((PropertyInfo)m.Member).PropertyType }, source.Expression, pred)
                  );
        }

        /*
        public static ISioFilter<T> AddTake<T>(this ISioFilter<T> source, int count)
        {
            ParameterExpression p = Expression.Parameter(typeof(T), "p");

            ConstantExpression pred = Expression.Constant(count);

            return source.CreateQuery<T>(
                   Expression.Call(typeof(FilterMethods), "Take", new Type[] { typeof(T) }, source.Expression, pred)
                   );
        }

        public static ISioFilter<T> AddSkip<T>(this ISioFilter<T> source, int count)
        {
            ParameterExpression p = Expression.Parameter(typeof(T), "p");

            ConstantExpression pred = Expression.Constant(count);

            return source.CreateQuery<T>(
                   Expression.Call(typeof(FilterMethods), "Skip", new Type[] { typeof(T) }, source.Expression, pred)
                   );
        }
        */

        public static ISioFilter<T> AddPropertyMatchCaseInsensitive<T>(this ISioFilter<T> source, string prop, object value)
        {
            ParameterExpression p = Expression.Parameter(typeof(T), "x");

            MemberExpression m = Expression.MakeMemberAccess(p, FindMemberField<T>(prop));

            Expression m2 = m;


            if (m.Type == typeof(String))
            {
                m2 = Expression.Call(
                       m, typeof(String).GetMethod("ToUpper", new Type[] { }));
                if (value != null) 
                    value = ((String)value).ToUpper();
            }


            if ((value == null) || (m.Type.IsValueType))
                value = Activator.CreateInstance(m.Type);


            LambdaExpression pred = Expression.Lambda(
                Expression.Equal(
                m2,
                Expression.Constant(value)
                ),
                p
            );

            return source.CreateQuery<T>(
                   Expression.Call(typeof(FilterMethods), "Where", new Type[] { typeof(T) }, source.Expression, pred)
                   );
        }

        public static ISioFilter<T> AddPropertyMatch<T>(this ISioFilter<T> source, string prop, object value)
        {
            ParameterExpression p = Expression.Parameter(typeof(T), "x");

            MemberExpression m = Expression.MakeMemberAccess(p, FindMemberField<T>(prop));

            if ((value == null) || (m.Type.IsValueType))
                value = Activator.CreateInstance(m.Type);

            LambdaExpression pred = Expression.Lambda(
                Expression.Equal(
                m,
                Expression.Constant(value)
                ),
                p
            );

            return source.CreateQuery<T>(
                   Expression.Call(typeof(FilterMethods), "Where", new Type[] { typeof(T) }, source.Expression, pred)
                   );
        }

        public static ISioFilter<T> AddPropertyDoesntMatch<T>(this ISioFilter<T> source, string prop, object value)
        {
            ParameterExpression p = Expression.Parameter(typeof(T), "p");

            MemberExpression m = Expression.MakeMemberAccess(p, FindMemberField<T>(prop));
            if ((value == null) || (m.Type.IsValueType))
                value = Activator.CreateInstance(m.Type);


            LambdaExpression pred = Expression.Lambda(
                Expression.NotEqual(
                m,
                Expression.Constant(value)
                ),
                p
            );

            return source.CreateQuery<T>(
                   Expression.Call(typeof(FilterMethods), "Where", new Type[] { typeof(T) }, source.Expression, pred)
                   );
        }

        public static ISioFilter<T> AddPropertyLessThen<T>(this ISioFilter<T> source, string prop, object value)
        {
            ParameterExpression p = Expression.Parameter(typeof(T), "p");

            MemberExpression m = Expression.MakeMemberAccess(p, FindMemberField<T>(prop));
            if ((value == null) || (m.Type.IsValueType))
                value = Activator.CreateInstance(m.Type);


            LambdaExpression pred = Expression.Lambda(
                Expression.LessThan(
                m,
                Expression.Constant(value)
                ),
                p
            );

            return source.CreateQuery<T>(
                   Expression.Call(typeof(FilterMethods), "Where", new Type[] { typeof(T) }, source.Expression, pred)
                   );
        }

        public static ISioFilter<T> AddPropertyMoreThen<T>(this ISioFilter<T> source, string prop, object value)
        {
            ParameterExpression p = Expression.Parameter(typeof(T), "p");

            MemberExpression m = Expression.MakeMemberAccess(p, FindMemberField<T>(prop));
            if ((value == null) || (m.Type.IsValueType))
                value = Activator.CreateInstance(m.Type);


            LambdaExpression pred = Expression.Lambda(
                Expression.LessThan(
                Expression.Constant(value),
                m
                ),
                p
            );

            return source.CreateQuery<T>(
                   Expression.Call(typeof(FilterMethods), "Where", new Type[] { typeof(T) }, source.Expression, pred)
                   );
        }
    }
}
